package com.upb.webserver.contract;

import com.upb.webserver.application.ApplicationManager;
import com.upb.webserver.chain.ChainService;
import com.upb.webserver.common.enums.ChainTypeEnum;
import com.upb.webserver.common.enums.ContractStatusEnum;
import com.upb.webserver.common.enums.ContractTypeEnum;
import com.upb.webserver.common.enums.PlatformEnum;
import com.upb.webserver.common.exception.WebServerException;
import com.upb.webserver.common.pojo.base.BasePageResponse;
import com.upb.webserver.common.pojo.contract.RspContractVO;
import com.upb.webserver.common.returncode.ConstantCode;
import com.upb.webserver.common.tools.JsonUtils;
import com.upb.webserver.common.tools.MybatisExampleTools;
import com.upb.webserver.company.CompanyDeveloperService;
import com.upb.webserver.dao.entity.TbContract;
import com.upb.webserver.dao.entity.TbContractExample;
import com.upb.webserver.dao.mapper.TbChainMapper;
import com.upb.webserver.dao.mapper.TbContractMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

@Slf4j
@Service
public class ContractManager {
    @Autowired
    private TbContractMapper contractMapper;
    @Autowired
    private CompanyDeveloperService companyDeveloperService;
    @Autowired
    private ApplicationManager applicationManager;

    @Autowired
    private ChainService chainService;
    @Autowired
    private TbChainMapper chainMapper;

    /**
     * @param appId
     * @param name
     * @param chainTypeEnum
     * @return
     */
    @Transactional
    public TbContract saveContract(int chainId, int appId, Integer remoteId, String name, ChainTypeEnum chainTypeEnum, ContractTypeEnum contractTypeEnum) {
        log.info("start exec method [saveContract]. chainId:{} appId:{} remoteId:{} name:{} chainTypeEnum:{} contractTypeEnum:{}", chainId, appId, remoteId, name, chainTypeEnum.getId(), contractTypeEnum);

        //check param
        TbContract contract = contractMapper.selectByApplicationAndName(appId, name);
        log.info("query contract by applicationId:{} and name:{}, the result:{}", appId, name, JsonUtils.objToString(contract));

        //add
        if (Objects.isNull(contract)) {
            //save
            TbContract tbContract = new TbContract();
            tbContract.setChainId(chainId);
            tbContract.setApplicationId(appId);
            tbContract.setName(name);
            tbContract.setChainType(chainTypeEnum.getId());
            tbContract.setContractType(contractTypeEnum.getId());
            tbContract.setStatus(ContractStatusEnum.NOT_DEPLOY.getId());
            tbContract.setCreateTime(new Date());
            tbContract.setModifyTime(new Date());
            contractMapper.insertSelective(tbContract);
            log.info("success exec method [saveContract].");
            return contractMapper.selectByPrimaryKey(tbContract.getId());
        }

        //check remoteId
        if (!Objects.equals(contract.getRemoteId(), remoteId)) {
            log.warn("fail exec method [saveContract]. found record by applicationId:{} and name:{} , and the contract remoteId is:{} ,but input remoteId:{}", appId, name, contract.getRemoteId(), remoteId);
            if (remoteId != null) {
                throw new WebServerException(ConstantCode.CONTRACT_ID_INVALID);
            }
            throw new WebServerException(ConstantCode.DUPLICATE_CONTRACT_NAME);
        }

        if (contract.getStatus() == ContractStatusEnum.DEPLOY_SUCCESS.getId()) {
            log.warn("fail exec method [saveContract]. found record by applicationId:{} and name:{} ,   but the contract has bean deploy", appId, name);
            throw new WebServerException(ConstantCode.NOT_SUPPORT_OPERATE);
        }

        //update
        log.info("contract:{} already exist,and old status is:{},update new status to:{}", name, contract.getStatus(), ContractStatusEnum.NOT_DEPLOY.getId());
        contract.setStatus(ContractStatusEnum.NOT_DEPLOY.getId());
        contract.setModifyTime(new Date());
        contractMapper.updateByPrimaryKey(contract);
        return contract;
    }


    /**
     * @param contractId
     * @param remoteId
     */
    @Transactional
    public void updateRemoteId(Integer contractId, Integer remoteId) {
        log.info("start exec method [updateRemoteId]. contractId:{} remoteId:{}", contractId, remoteId);

        //check
        if (Objects.isNull(remoteId)) {
            log.info("start exec method [updateRemoteId]. remoteId:{}", remoteId);
            throw new WebServerException(ConstantCode.CONTRACT_SAVE_NOT_SUCCESS);
        }

        //save
        TbContract contract = verifyContractId(contractId);
        contract.setRemoteId(remoteId);
        contract.setModifyTime(new Date());
        contractMapper.updateByPrimaryKey(contract);
        log.info("success exec method [updateRemoteId]. contractId:{} remoteId:{}", contractId, remoteId);

    }

    /**
     * @param contractId
     * @return
     */
    public TbContract verifyContractId(Integer contractId) {
        log.info("start exec method [verifyContractId]. contractId:{} remoteId:{}", contractId);
        TbContract contract = contractMapper.selectByPrimaryKey(contractId);
        if (Objects.isNull(contract)) {
            log.warn("fail exec method [verifyContractId]. not found record by contractId:{}", contractId);
            throw new WebServerException(ConstantCode.CONTRACT_ID_INVALID);
        }
        log.info("success exec method [verifyContractId]. contractId:{} remoteId:{}", contractId);
        return contract;
    }


//    /**
//     * @param platform
//     * @param currentAccountId
//     * @param pageNumber
//     * @param pageSize
//     * @param chainId
//     * @param chainType
//     * @param applicationId
//     * @return
//     */
//    public BasePageResponse queryByPage(Byte platform, int currentAccountId, int pageNumber, int pageSize, Integer chainId, Byte chainType, Integer applicationId) {
//        log.info("start exec method [queryByPage].platform:{} currentAccountId:{} pageNumber:{} pageSize:{} chainId:{} chainType:{} applicationId:{}", platform, currentAccountId, pageNumber, pageSize, chainId, chainType, applicationId);
//
//        TbContractExample example = null;
//        if (PlatformEnum.DEVELOPER.getId() == platform) {
//            example = initExampleForDeveloper(currentAccountId, pageNumber, pageSize, chainId, chainType, applicationId);
//        } else {
//            example = MybatisExampleTools.initSamplePageExample(TbContractExample.class, pageNumber, pageSize, new TbContract(chainId, applicationId, chainType, null));
//        }
//        if (Objects.isNull(example)) {
//            log.info("finish exec method[queryByPage] ,initExample result is null");
//            return BasePageResponse.initialize();
//        }
//
//        BasePageResponse basePageResponse = BasePageResponse.initialize();
//        basePageResponse.setTotalCount(contractMapper.countByExample(example));
//        List<RspContractVO> rspContractVOList = new ArrayList<>();
//        if (basePageResponse.getTotalCount() > 0) {
//            example.setOrderByClause(Constant.ORDER_BY_MODIFY_TIME_DESC);
//            List<TbContract> tbContractList = contractMapper.selectByExample(example);
//            tbContractList.stream()
//                    .forEach(tbContract -> {
//                        RspContractVO rspContractVO = new RspContractVO();
//                        BeanUtils.copyProperties(tbContract, rspContractVO);
//                        rspContractVO.setChainName(chainMapper.selectNameByPrimaryKey(tbContract.getChainId()));
//                        TbApplication tbApplication = applicationManager.verifyApplicationId(tbContract.getApplicationId());
//                        rspContractVO.setApplicationName(tbApplication.getName());
//                        rspContractVO.setApplicationNameZh(tbApplication.getNameZh());
//                        rspContractVOList.add(rspContractVO);
//                    });
//        }
//        basePageResponse.setData(rspContractVOList);
//        log.info("success exec method [queryByPage]. basePageResponse:{}", JsonUtils.objToString(basePageResponse));
//        return basePageResponse;
//    }
//
//
//    /**
//     * @param currentAccountId
//     * @param pageNumber
//     * @param pageSize
//     * @param chainId
//     * @param chainType
//     * @param applicationId
//     * @return
//     */
//    private TbContractExample initExampleForDeveloper(int currentAccountId, int pageNumber, int pageSize, Integer chainId, Byte chainType, Integer applicationId) {
//        log.info("start exec method [initExampleForDeveloper]. currentAccountId:{} pageNumber:{} pageSize:{} chainId:{} chainType:{} applicationId:{}", currentAccountId, pageNumber, pageSize, chainId, chainType, applicationId);
//        int companyId = companyDeveloperService.selectCompanyIdByDeveloperId(currentAccountId);
//        Set<Integer> appIdSet = applicationManager.getApplicationIdSetByCompanyId(companyId);
//        Set<Integer> chainIdSet = chainService.getChainIdSetByCompanyId(companyId);
//
//        if (CollectionUtil.isEmpty(chainIdSet) && CollectionUtil.isEmpty(appIdSet)) {
//            log.info("not found application or chain by company:{}", companyId);
//            return null;
//        }
//
//        //param:chainId
//        if (chainId != null) {
//            if (CollectionUtil.isNotEmpty(chainIdSet) && !chainIdSet.contains(chainId)) {
//                log.warn("fail exec method [initExampleForDeveloper]. inputChainId:{} but support:{}", chainId, JsonUtils.objToString(chainIdSet));
//                throw new WebServerException(ConstantCode.INVALID_CHAIN_ID);
//            }
//            chainIdSet = SetUtils.hashSet(chainId);
//        }
//
//        //param:applicationId
//        if (applicationId != null) {
//            if (CollectionUtil.isNotEmpty(appIdSet) && !appIdSet.contains(applicationId)) {
//                log.warn("fail exec method [initExampleForDeveloper]. inputApplicationId:{} but support:{}", applicationId, JsonUtils.objToString(chainIdSet));
//                throw new WebServerException(ConstantCode.APPLICATION_ID_INVALID);
//            }
//            appIdSet = SetUtils.hashSet(applicationId);
//        }
//
//
//        //query param
//        TbContractExample example = MybatisExampleTools.initSamplePageExample(TbContractExample.class, pageNumber, pageSize, new TbContract(null, chainType, null));
//        TbContractExample.Criteria criteria = example.createCriteria();
//        if (CollectionUtil.isNotEmpty(chainIdSet)) {
//            criteria.andChainIdIn(CollectionUtil.newArrayList(chainIdSet));
//        }
//        if (CollectionUtil.isNotEmpty(appIdSet)) {
//            criteria.andApplicationIdIn(CollectionUtil.newArrayList(appIdSet));
//        }
//
//        example.getOredCriteria().add(criteria);
//        log.info("success exec method[initExampleForDeveloper]. result:{}", JsonUtils.objToString(example));
//        return example;
//    }
//

    /**
     * verify remote contract id.
     *
     * @param chainTypeEnum
     * @param remoteContractId
     * @return
     */
    public TbContract verifyContractRemoteId(ChainTypeEnum chainTypeEnum, int remoteContractId) {
        log.info("start exec method[verifyContractRemoteId]. chainTypeEnum:{} remoteContractId:{}", JsonUtils.objToString(chainTypeEnum), remoteContractId);

        TbContractExample example = MybatisExampleTools.initSampleExample(TbContractExample.class, new TbContract(null, chainTypeEnum.getId(), remoteContractId));
        Optional<TbContract> contractOpt = contractMapper.getOneByExample(example);
        if (!contractOpt.isPresent()) {
            log.info("fail exec method [verifyContractRemoteId]. invalid remote id:{}", remoteContractId);
            throw new WebServerException(ConstantCode.CONTRACT_ID_INVALID);
        }
        TbContract contract = contractOpt.get();
        log.info("success exec method[verifyContractRemoteId]. chainTypeEnum:{} remoteContractId:{} ,result:{}", JsonUtils.objToString(chainTypeEnum), remoteContractId, JsonUtils.objToString(contract));
        return contract;
    }
}

